package com.szis.auth.common;

import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 通过redis来完成服务节点的管理
 *
 * @author: yxl
 * 2025-07-18
 */

@Component
public class RedisServerManager implements ServerManager {

    public static final String server_key_prefix = "server:";

    @Resource
    private RedisTemplate redisTemplate;

    private ServerNodeInfo server;

    @Override
    public void set(ServerNodeInfo server) {
        this.server = server;
    }

    @Override
    public ServerNodeInfo get() {
        return server;
    }

    @Override
    public List<ServerNodeInfo> listServers(String serverName) {
        List<ServerNodeInfo> servers = new ArrayList<>();
        String key = server_key_prefix + serverName;
        Set<String> nodes = redisTemplate.opsForHash().keys(key);

        if (CollectionUtils.isNotEmpty(nodes)) {
            nodes.forEach(node -> {
                String[] parts = node.split(":");
                ServerNodeInfo info = new ServerNodeInfo(serverName, parts[0], Integer.parseInt(parts[1]));
                servers.add(info);
            });
        }
        return servers;
    }

    @Override
    public void register(ServerNodeInfo server){
        String luaScript = """
                local key = KEYS[1]
                local field = string.gsub(ARGV[1], "\\"", "")
                                
                -- 检查是否已存在
                if redis.call('hexists', key, field) == 1 then
                    return -1
                end
                                
                -- 获取所有已分配的 workerId
                local values = redis.call('hvals', key)
                                
                -- 找出最小缺失的数字
                local expected = 1
                table.sort(values)
                for _, v in ipairs(values) do
                    if tonumber(v) == expected then
                        expected = expected + 1
                    elseif tonumber(v) > expected then
                        break
                    end
                end
                                
                -- 注册新服务
                redis.call('hset', key, field, expected)
                                
                return expected
                """;

        String key = "server:" + server.getServerName();
        String field = server.getHost() + ":" + server.getPort();

        DefaultRedisScript<Integer> script = new DefaultRedisScript<>();
        script.setScriptText(luaScript);
        script.setResultType(Integer.class);

        long workerId = (Long) redisTemplate.execute(script, List.of(key), field);

        if (workerId == -1) {
            throw new RuntimeException("服务已注册");
        }
    }

    @Override
    public void unregister(ServerNodeInfo server) {
        String key = server_key_prefix + server.getServerName();
        String field = server.getHost() + ":" + server.getPort();
        redisTemplate.opsForHash().delete(key, field);
    }

    @Override
    public int workerId(String serverName, ServerNodeInfo server) {
        String key = server_key_prefix + server.getServerName();
        String field = server.getHost() + ":" + server.getPort();
        return (int) redisTemplate.opsForHash().get(key, field);
    }

}
